江河分流:分库分表之Sharding-JDBC

序言

  随着业务数据量不断增长,单表数据量过大容易带来索引膨胀、查询变慢、写入压力集中等问题。当数据规模达到一定程度后,分库分表就成为常见的数据库水平扩展方案。

  但真正开始接触 ShardingSphere 后,很多人会发现,分库分表本身并不难,难的是理解它背后的路由模型:什么是逻辑库、物理库、逻辑表和物理表?分片键应该如何选择?分片策略和分片算法到底有什么区别?为什么同样是一个 employee_id,既可以用于分库,又可以用于分表?StandardComplexHintInlineNone 这些策略又分别解决什么问题?

  更进一步,当 SQL 中出现 =INBETWEEN> 等不同条件时,ShardingSphere 又是如何识别分片条件,并最终计算出应该访问哪些库、哪些表的?

  本文不单纯从配置项出发,而是尝试沿着一条完整的「分片概念 → 分片策略 → 分片算法 → SQL 路由 → 版本演进 → 实战案例」路径,把 ShardingSphere 的分片机制串起来理解。文章还会结合 GPS 轨迹这类典型的大数据量业务场景,演示员工维度、时间维度以及复合维度下的不同分库分表方案,并进一步讨论实际设计中容易出现的分片空洞、数据分布不均以及库表如何合理组合等问题。

  希望通过这些案例,最终建立起一个整体认知,能够从“数据为什么要这样路由”的角度理解它的设计。

ShardingJDBC 简介

  ShardingJDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

  • 适用于任何基于 JDBC 的ORM框架,如:JPA, Hibernate, Mybatis, SpringJDBC Template 或直接使用 JDBC;
  • 支持任何第三方的数据库连接池,如:DBCP,C3PO,BoneCP,HikariCP 等;
  • 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL, Oracle,SQLServer 以及任何可使用 JDBC 访问的数据库。

核心概念

  要理解分库分表,核心需要记住一些概念:逻辑库、物理库、逻辑表、物理表、分片策略。

逻辑库和物理库

逻辑库

  逻辑库是应用程序看到的数据库,是对多个物理数据库的抽象。

  在代码中,我们仍然可以按照普通数据库的方式进行开发。

  例如在 MyBatis-Plus 仍然可以执行SELECT * FROM t_gps_track;这个 SQL 去查询轨迹数据,应用并不需要知道gps_001gps_002gps_003这些具体数据库。

物理库

  物理库是真实存在的 MySQL 数据库。

  例如gps_001gps_002两个数据库,它们可能分别运行在相同的 MySQL 实例上,也可能运行在不同的 MySQL 实例上,比如 MySQL-01 实例存放了gps_001库,而gps_002库位于MySQL-02实例上。

逻辑库 VS 物理库

  简而言之,逻辑库是对外统一的数据库概念,而物理库才是真正存储数据的 MySQL 数据库。

逻辑表和物理表

  数据库层面解决了之后,表同样需要进行拆分。

  如果未经过分库分表处理,原来我们只有一张单表:t_gps_track,在 Sharding-JDBC 分库分表后,比如说分为了t_gps_track_01t_gps_track_02t_gps_track_03三张表。

逻辑表

  那么,分库分表后,从 Java 应用角度看SELECT * FROM t_gps_track WHERE user_id = 1001;,应用程序永远操作的是t_gps_track这张表,虽然这张表实际上并不存在,但是此时它逻辑上代表了拆分过的多张表,此时t_gps_track就被称为逻辑表

物理表

  t_gps_track_01t_gps_track_02t_gps_track_03这三张拆分的表,就叫做物理表。

逻辑表 VS 物理表

  逻辑表实际上并不存在,是应用程序看到的表,真正存储数据的表叫物理表

分片策略

  到这里,我们知道了逻辑库、物理库、逻辑表、物理表这些概念,但是还有一个最重要的问题:你打算怎么把这些数据拆开?即到底应该对数据以什么作为依据,通过什么样的算法,分到哪个数据库、哪张表?

  这由分片策略决定,它主要回答几个问题:

  • 按哪个字段拆?
  • 用哪种算法去计算?
  • 拆成多少个库、多少张表?

  这几个问题,引出了新的三个概念,分片策略本质上是这三个概念的组合,下面我们来分别探讨下它们。

三个概念

分片键

  从需要分库分表的表结构中,可以选择一个或多个字段充当数据的拆分依据,选择的相关字段被称为分片键。

  举个例子,现在有一张 GPS 轨迹表,其字段包括:iduser_idlongitudelatitudecreate_time这些字段,当我们:

  • 选择单一的字符字段,比如user_id作为拆分依据,那么user_id就是分片键
  • 选择单一的时间字段,比如create_time作为拆分依据,那么create_time就是分片键
  • 选择复合的多个字段,比如user_id + create_time作为拆分依据,那么user_id + create_time就是复合的分片键

分片算法

  当选择完分片键后,需要通过某种算法将数据分为多片,这就是分片算法,常见的分片算法如下表:

分片算法 说明 优点 缺点
哈希取模 用某个数字取余数,像“轮流分班” 数据分布均匀 扩容时要重新分,迁移成本高
一致性哈希 把所有分片放在一个环上,数据顺时针找最近的分片 扩容时只影响少量数据 实现复杂,可能出现数据倾斜
范围分片 按数值范围划分,例如 ID 1~100万去 1 库 查询范围方便 容易产生热点库
枚举/列表分片 按固定值映射,例如北京去 1 库,上海去 2 库 简单直观 只适合固定分类的字段
日期分片 按月/天拆分,例如订单表按月分表 时间范围查询方便 历史数据可能冷热不均
复合分片 多个字段一起决定位置,例如先按地区,再按用户 ID 哈希 更灵活 规则复杂

  实际项目中,很多时候不是只用一种,而是组合使用。

库级/表级分片策略

  分库分表,分库分表,其实存在三种组合:

  • 只分库
  • 只分表
  • 既分库又分表

  因此,我们在讨论分片策略时,有库级的分片策略和表级的分片策略之说,所以,在实际的业务过程中,会灵活的组合运用,需要注意这两个维度的差异。

小节

  分片策略,是分库分表的核心,简而言之就是一句话:我们需要根据业务「选择合适的分片键 + 选择合适的分片算法」决定「数据去往哪个库或者哪张表」。

ShardingSphere 分片策略

  ShardingSphere 提供了 standard、complex、hint、inline、none 5 种分片策略。

分片策略 核心定义 分片键 SQL 是否需要携带分片键 典型场景
standard 标准 单分片键,按照统一的标准规则进行路由 单个 需要 user_idorder_idtenant_id 等单字段分片
complex 复合 多个分片键共同参与路由 多个 需要 user_id + create_time 等组合分片
inline 行表达式 将简单分片算法直接写成 Groovy 表达式 单个 需要 user_id % 2order_id % 4 等简单规则
hint 强制 不从 SQL 中获取分片键,而由业务代码直接指定分片 没有固定 SQL 分片键 不需要 按登录用户、租户、地域、业务上下文路由
none 不分片 明确指定当前逻辑表不进行分片 某张表只存在一个库/表,或广播/普通表

  在实际使用中,ShardingSphere 的分片策略可以分别作用于分库分表两个维度:

  • 分库策略(database-strategy:根据分片键和分片算法,决定数据应该路由到哪个物理库(数据源), 完整属性示例:
    • spring.shardingsphere.rules.sharding.tables.xxx.database-strategy
  • 分表策略(table-strategy:根据分片键和分片算法,决定数据应该路由到哪个物理表,完整属性示例:
    • spring.shardingsphere.rules.sharding.tables.xxx.table-strategy

  下面为一个 gps 轨迹表分别配置库分库策略和分表策略样例的 demo 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 作用的库和表
spring.shardingsphere.rules.sharding.tables.gps_track.actual-data-nodes=ds$->{0..1}.gps_track$->{20260726..20260806}
# 库级别分片配置:指定库级别分片策略、分片键、分片算法名称
spring.shardingsphere.rules.sharding.tables.gps_track.database-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.gps_track.database-strategy.standard.sharding-algorithm-name=algo-db-eid

# 表级别分片配置:指定表级别分片策略、分片键、分片算法名称
spring.shardingsphere.rules.sharding.tables.gps_track.table-strategy.standard.sharding-column=gps_time
spring.shardingsphere.rules.sharding.tables.gps_track.table-strategy.standard.sharding-algorithm-name=algo-day-interval

# 库级别分片使用的算法的具体配置
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-db-eid.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-db-eid.props.algorithm-expression=ds$->{employee_id % 2}

# 表级别分片使用的算法的具体配置
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.type=INTERVAL
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-pattern=yyyy-MM-dd HH:mm:ss
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-lower=2026-07-26 00:00:00
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-upper=2026-08-07 00:00:00
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.sharding-suffix-pattern=yyyyMMdd
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-interval-unit=DAYS

五种分片策略

标准分片策略

  标准分片策略(standard)适用于具有单一分片键的标准分片场景。

  standard 策略支持精确分片,即在 SQL 中包含=、in 操作符,以及范围分片,包括 BETWEEN AND、>、<、>=、<= 等范围操作符。

  该策略下有两个属性,分片字段 shardingColumn 和分片算法名 shardingAlgorithmName。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
shardingsphere:
rules:
sharding:
tables:
t_order: # 逻辑表名称
# 数据节点:数据库.分片表
actual-data-nodes: db$->{0..1}.t_order_${1..10}
# 分库策略
databaseStrategy:
standard: # 用于单分片键的标准分片场景
shardingColumn: order_id # 分片列名称
shardingAlgorithmName: # 分片算法名称
tableStrategy: # 分表策略,同分库策略

行表达式分片策略

  行表达式分片策略(inline)适用于具有单一分片键的简单分片场景,支持 SQL 语句中 =、IN 操作符。

  inline 策略支持在配置属性 algorithm-expression 中书写 Groovy 表达式,用来定义对分片健的运算逻辑,无需单独定义分片算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
shardingsphere:
rules:
sharding:
tables:
t_order: # 逻辑表名称
# 数据节点:数据库.分片表
actual-data-nodes: db$->{0..1}.t_order_${1..10}
# 分库策略
databaseStrategy: # 分库策略
inline: # 行表达式类型分片策略
algorithm-expression: db$->{order_id % 2} # Groovy 表达式
tableStrategy: # 分表策略,同分库策略

复合分片策略

  复合分片策略(complex)适用于多个分片键的复杂分片场景,属性 shardingColumns 中多个分片健以逗号分隔。支持 SQL 语句中 >、>=、<=、<、=、IN 和 BETWEEN AND 等操作符。

  比如:我们希望通过 user_id 和 order_id 等多个字段共同运算得出数据路由到具体哪个分片中,就可以应用该策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
shardingsphere:
rules:
sharding:
tables:
t_order: # 逻辑表名称
# 数据节点:数据库.分片表
actual-data-nodes: db$->{0..1}.t_order_${1..10}
# 分库策略
databaseStrategy: # 分库策略
complex: # 用于多分片键的复合分片场景
shardingColumns: order_id,user_id # 分片列名称,多个列以逗号分隔
shardingAlgorithmName: # 分片算法名称
tableStrategy: # 分表策略,同分库策略

Hint 分片策略(了解)

  Hint 强制分片策略相比于其他几种分片策略稍有不同,该策略无需配置分片健,由外部指定分库和分表的信息,可以让 SQL 在指定的分库、分表中执行。

  使用场景:

  • 强制在指定数据库进行某些数据操作
  • 分片字段不存在 SQL 和数据库表结构中,而存在于外部业务逻辑

  比如,我们希望用 user_id 做分片健进行路由订单数据,但是 t_order 表中也没 user_id 这个字段啊,这时可以通过 Hint API 手动指定分片库、表等信息,强制让数据插入指定的位置。

不分片策略

  不分片策略,字如其意,如果设置了该策略,那么对逻辑表的所有操作将会执行全库表路由。具体配置属性如下:

  • spring.shardingsphere.rules.sharding.tables.xxx.database-strategynone
  • spring.shardingsphere.rules.sharding.tables.xxx.table-strategynone

不同分片策略下的路由流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
                       SQL


┌───────────────┐
│ 路由入口 │
└───────┬───────┘

┌─────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
Standard Complex Hint
│ │ │
提取一个键 提取多个键 从程序获取
│ │ │
▼ ▼ ▼
StandardStrategy ComplexStrategy HintStrategy
│ │ │
▼ ▼ ▼
标准算法 复合算法 Hint算法
│ │ │
└─────────────────┼──────────────────┘


路由结果

┌────────┴────────┐
▼ ▼
数据库 数据表


Inline None
│ │
▼ ▼
表达式计算 不分片
│ │
▼ ▼
路由结果 原表

ShardingSphere 分片算法

  对 ShardingSphere 而言,部分常用的分片策略无法单独使用,需要搭配不同的分片算法才能生效。

  在 ShardingSphere 中,提供了多种内置分片算法,可以根据业务的分片规则选择合适的算法。常见算法如下:

算法 适合场景 特点 生产建议
MOD(了解) user_idorder_id 这类数值型分片键 直接取模运算,分布较均匀 ⭐⭐
INLINE(了解) 简单规则,例如 user_id % 4 通过表达式直接计算分片位置 ⭐⭐⭐
HASH_MOD 用户、订单、设备等 ID 型数据 先 Hash 再取模,分布通常更均匀 ⭐⭐⭐⭐⭐
VOLUME_RANGE 希望每个分片控制在大致固定数据量 根据数据量划分范围 ⭐⭐
BOUNDARY_RANGE 按 ID、金额、时间等划固定区间 0~100万 → 表1100万~200万 → 表2 ⭐⭐⭐
INTERVAL 按固定时间周期分片 如每月一张表、每天一张表 ⭐⭐⭐⭐
AUTO_INTERVAL 按时间自动划分,例如日志、流水 时间范围分片,配置相对灵活 ⭐⭐⭐⭐
HINT_INLINE SQL 中没有合适的分片键,但业务代码知道路由信息 通过 Hint 强制指定分片 ⭐⭐
COMPLEX_INLINE 多个分片键共同参与路由 例如 tenant_id + user_id ⭐⭐
CLASS_BASED 分片规则比较复杂,内置算法无法满足 自己写 Java 分片算法 ⭐⭐⭐⭐

MOD(了解)

  MOD 比较适合连续自增或均匀增长的分片键

  MOD 对分片键进行取模的计算公式为:分片 = sharding_value % 分片数量

  例如:order_id % 4,最终将数据分散到:t_order_0t_order_1t_order_2t_order_3,适合 ID 分布比较均匀的场景。

INLINE(了解)

  适合简单、固定的取模分片规则

  具体规则是通过 Groovy 表达式直接计算目标库表。例如:algorithm-expression: t_order_${order_id % 4}表示:order_id = 10011001 % 4 = 1t_order_1

HASH_MOD(常用)

  HASH_MOD 会先对分片键进行哈希,再取模,其计算公式为hash(sharding_value) % 分片数量

  例如:user_idHash % 80 ~ 7

VOLUME_RANGE

略。

BOUNDARY_RANGE

  BOUNDARY_RANGE 分片算法适合按照连续数值范围进行分片的场景。

  具体规则为按照预先定义的范围边界进行路由。

  例如:

  • 0 ~ 999999 → t_order_0
  • 1000000 ~ 1999999 → t_order_1
  • 2000000 ~ 2999999 → t_order_2

INTERVAL / AUTO_INTERVAL(常用)

  INTERVAL / AUTO_INTERVAL主要用于时间分片

  非常适合订单、日志、GPS轨迹、操作记录、监控数据这类随着时间不断增长的数据
  例如按照月份:

  • 2026-01 → t_order_202601
  • 2026-02 → t_order_202602
  • 2026-03 → t_order_202603

  INTERVAL 可以按照指定的时间范围和时间间隔进行分片;AUTO_INTERVAL 则根据时间范围、间隔等配置自动计算分片。

HINT_INLINE(了解)

  HINT 比较特殊,它不是从 SQL 中解析分片键,而是由业务代码主动告诉 ShardingSphere:当前请求 → tenant_001,然后根据这个 Hint 进行路由。

  适用于:

  • SQL 中没有分片键
  • 根据登录用户路由
  • 根据租户路由
  • 根据地域路由
  • 根据业务上下文路由

COMPLEX_INLINE

  略

CLASS_BASED

  CLASS_BASED 可以支持使用自己编写的 Java 类去实现分片算法。

  例如业务规则比较复杂:tenant_idcity_idcreate_time → 自定义算法 → 计算目标库表

  当内置算法无法满足业务需求时,可以使用这种方式。

疑问

  ShardingSphere 分片策略和分片算法初看很难理解,比如我们会有以下疑问:

  • 它们有什么区别呢?
  • 为什么要分这么多的分片策略?
  • 不同分片策略如何支持的精确查询、范围查询?
  • 策略、匹配条件、算法逻辑上是怎么关联的?
  • 什么时候需要使用自定义分片算法?怎么定义?

  下面我们分别

策略与算法的区别

  从定义而言:分片策略决定“怎么接收分片条件”,分片算法决定“拿到分片值之后怎么算出目标库/表”。

  可以抽象成:

1
2
3
4
5
6
7
8
9
10
11
SQL / Hint

分片策略 Strategy

提取分片键、判断操作类型

分片算法 Algorithm

计算应该去哪些库 / 表

最终路由结果

  例如SELECT * FROM t_order WHERE order_id = 1001;这条 SQL,假设分片键为order_id,分片策略为 Standard,若 order_id = 1001,则可以调用具体的分片算法,比如1001 % 4 = 1,路由到t_order_1查询。

这里:

  • Standard —— 策略
  • order_id —— 分片键
  • 1001 % 4 —— 算法
  • t_order_1 —— 最终路由结果

为什么要对单键多键制定两种策略?

  ShardingSphere 常见的策略包括:

策略 核心解决什么问题
Standard 单分片键,按 SQL 中的分片条件路由
Complex 多个分片键共同参与路由
Inline 单分片键 + 简单表达式直接计算
Hint 不依赖 SQL 中的分片键,由 Hint 指定路由依据
None 不进行分片

  那么,为什么要区分单键、多键、甚至无键?

  这本质上是因为:单键和多键的“路由输入”不同,ShardingSphere 需要用不同的接口模型把分片键传给算法。

  官方文档也明确把两者区分为:

  • StandardShardingStrategy单个分片键
  • ComplexShardingStrategy多个分片键

  可以从“算法到底需要什么输入”来理解。

单键:一个值就可以决定去哪

  比如下表按照 user_id 分表(规则user_id % 4):

1
2
3
4
5
CREATE TABLE order (
id BIGINT,
user_id BIGINT,
...
);

  如果执行 SQLSELECT * FROM order WHERE user_id = 1001;,ShardingSphere 只需要拿到分片键user_id和分片值1001,算法计算1001 % 4 = 1得到结果,于是路由到表order_1

  所以单键策略非常简单:执行 SQL → 找到user_id→ 拿到 1001 → 分片算法计算 → 路由到order_1

  这就是 StandardShardingStrategy 适合的场景,它就是针对单分片键设计的。

多键:算法可能需要多个值共同决定

  在人员轨迹表中假设按照employee_id + create_time进行分片。

  例如:employee_id → 决定分库,create_time → 决定分表,SQL 为:

1
2
3
4
5
SELECT *
FROM gps_track
WHERE employee_id = 1001
AND create_time >= '2026-09-01'
AND create_time < '2026-10-01';

  这时候路由需要考虑:

1
2
employee_id = 1001
create_time = 2026-09-01 ~ 2026-10-01

  此时,两个分片键承担不同作用:

  • employee_id:决定数据库
  • create_time:决定具体月份/日期表

  所以算法需要同时获得:

1
2
3
4
{
employee_id: 1001,
create_time: 2026-09-01 ~ 2026-10-01
}

  然后由复杂分片算法自己决定:

1
2
3
4
5
6
7
ds_1
├── gps_track_20260901
├── gps_track_20260902
└── ...

ds_2
├── ...

  这就是 ComplexShardingStrategy 的意义:允许算法同时接收多个分片键以及它们对应的条件。 官方文档也说明复杂策略将多个分片键及其操作组合交给算法处理。

为什么不统一成“一个策略”?

  其实可以把它设计成一个统一接口,但会带来两个问题。

第一:单键场景会变复杂

  绝大多数简单分库分表其实都是:

  • user_id % 4
  • order_id % 8
  • tenant_id % 16

  如果全部设计成Map<分片键, 分片值>当然也能实现,但是对于user_id: 1001这种简单场景,接口就显得比较重。

  所以 ShardingSphere 把最常见的情况单独抽象出来:Standard 针对一个分片键简单路由。

第二:多键的组合关系无法统一规定

  假设有两个分片键tenant_iduser_id到底逻辑上应该怎么计算?

  可能是tenant_id % 4,也可能是user_id % 8,甚至(tenant_id + user_id) % 16,甚至tenant_id + user_id + create_time,或者:

  • tenant_id → 分库
  • user_id → 分表

  多种可能共同决定路由时,这些业务规则没有一个通用答案。

  因此 ShardingSphere 不替你规定多个分片键应该怎么组合,而是把这些信息交给 ComplexShardingAlgorithm,由你自己实现。

精确查询还是查询匹配?

  在分片策略中,不能简单地说:Standard 支持范围查询,Complex 不支持范围查询,Inline 不支持范围查询。

  更准确的是:不同策略能够接收不同类型的分片条件,而具体是否能够根据单值或范围条件进行“路由”,还取决于对应的分片算法是怎样实现的。

  为了支持单值或范围条件,分片算法可能是用接口继承的方式实现,也可能是用参数传递的方式实现,这在不同版本中实现上存在差异。

  例如WHERE order_id = 1001是精准条件=,而:WHERE order_id BETWEEN 1000 AND 2000是范围条件BETWEEN,二者对分片算法的要求不同。

Standard 策略下的匹配

  Standard 的核心特点:单分片键 + 区分精准查询和范围查询。

  例如分片键为order_id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
                    SQL


提取分片条件


StandardShardingStrategy

判断条件类型

┌──────────┴──────────┐
↓ ↓
精确条件 范围条件
= / IN 等 BETWEEN / > / <
│ │
↓ ↓
PreciseShardingValue RangeShardingValue
│ │
└──────────┬──────────┘

StandardShardingAlgorithm

执行具体算法

┌──────────┴──────────┐
↓ ↓
t_order_1 t_order_0、t_order_1...


最终路由

① 精准查询

  如果查询 SQL 为WHERE order_id = 1001,Standard 策略发现分片键order_id,操作为=,那么会交给精准分片算法(根据哈希、范围分片算法存在多种实现),最终路由到t_order_1

② 范围查询

  如果查询 SQL 为WHERE order_id BETWEEN 1000 AND 2000,这时候不能简单计算1000 % 4,因为逻辑上需要判断下面的这些值分别可能落在哪些分片:

1
2
3
4
5
1000
1001
1002
...
2000

  因此 Standard 可以配合范围分片算法(根据哈希、范围分片算法存在多种实现)处理范围条件。

精准、范围查询的版本差异

  在 ShardingSphere 不同版本中,对于精准、范围查询的架构上处理不太一样。

版本 算法组织方式 Standard 典型配置
3.x Precise / Range 等独立接口 直接配置算法类
4.x 仍以独立算法接口为主 preciseAlgorithmClassName + rangeAlgorithmClassName
5.x 统一 ShardingAlgorithm + 算法类型/名称 shardingAlgorithmName + type

  3.x/4.x 官方文档明确把 PreciseShardingAlgorithmRangeShardingAlgorithmComplexKeysShardingAlgorithm 分开定义。

  5.x 则将内置算法按自动分片、标准、复合、Hint、Class Based 等类别组织,并通过算法类型配置使用。

4.x 的设计:Strategy 直接绑定算法

  以 ShardingSphere 4.1 为例:

1
2
3
4
5
StandardShardingStrategyConfiguration(
String shardingColumn,
PreciseShardingAlgorithm<?> preciseShardingAlgorithm,
RangeShardingAlgorithm<?> rangeShardingAlgorithm
)

  也就是说,Standard Strategy 本身就知道:精确算法是谁、范围算法是谁,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
                ShardingStrategy

StandardShardingStrategy

┌─────────┴─────────┐
↓ ↓
PreciseShardingAlgorithm RangeShardingAlgorithm
(接口) (接口)
↑ ↑
│ │
┌─────────┼────────┐ ┌──────┼──────┐
↓ ↓ ↓ ↓ ↓ ↓
精确取模 精确哈希 自定义 ID范围 时间范围 自定义
算法实现 算法实现 算法实现 算法实现 算法实现 算法实现

  配置也比较直接:

1
2
3
4
standard:
sharding-column: user_id
precise-algorithm-class-name: com.xxx.UserPreciseAlgorithm
range-algorithm-class-name: com.xxx.UserRangeAlgorithm

  官方 4.1 配置就是这种模式:precise-algorithm-class-name 对应 PreciseShardingAlgorithmrange-algorithm-class-name 对应 RangeShardingAlgorithm

4.x 的问题:算法和 Strategy 耦合比较明显

  假设:

1
2
3
4
Standard Strategy

├── PreciseAlgorithm A
└── RangeAlgorithm B

  如果以后又增加一种算法:HashAlgorithmIntervalAlgorithm……
,Strategy 层就需要越来越多地认识各种算法接口,所以整体结构容易变成:

1
2
3
4
5
6
7
Strategy

├── PreciseShardingAlgorithm
├── RangeShardingAlgorithm
├── ComplexKeysShardingAlgorithm
├── HintShardingAlgorithm
└── ...

  这会让:“分片策略怎么组织路由”和:“具体算法怎么计算分片”之间产生比较强的耦合。

5.x 的核心变化:统一成 ShardingAlgorithm

  到了 ShardingSphere 5.x,核心抽象变成org.apache.shardingsphere.sharding.spi.ShardingAlgorithm,于是结构变成:

1
2
3
4
5
6
7
           ShardingAlgorithm

┌───────────────┼───────────────┐
↓ ↓ ↓
INLINE MOD INTERVAL
│ │ │
表达式 取模 时间

  这时候 Standard Strategy 不再需要:“我要找一个 PreciseShardingAlgorithm”、“我要找一个 RangeShardingAlgorithm”,而是:“我有一个 shardingAlgorithmName,去找到这个算法。”

  例如:

1
2
3
standard:
sharding-column: worker_id
sharding-algorithm-name: worker-db-inline

  然后:

1
2
3
4
5
sharding-algorithms:
worker-db-inline:
type: INLINE
props:
algorithm-expression: ds_${worker_id % 4}

  先给结论:3.x/4.x 更像是“策略直接持有具体算法对象/算法类”;5.x 更像是“策略只引用一个算法名称 → 算法注册中心根据 type 找到实现 → 初始化算法 → Strategy 调用统一的 ShardingAlgorithm 接口”。

4.x 到 5.x 配置的重大变化

  以前重点是:配置 → Java Class:

1
2
3
4
standard:
sharding-column: worker_id
precise-algorithm-class-name: com.xxx.MyPreciseAlgorithm
range-algorithm-class-name: com.xxx.MyRangeAlgorithm

  现在先配置策略:

1
2
3
standard:
sharding-column: worker_id
sharding-algorithm-name: worker-db-inline

  然后配置算法:

1
2
3
4
5
sharding-algorithms:
worker-db-inline:
type: INLINE
props:
algorithm-expression: ds_${worker_id % 4}

  重点变成:Strategy → 算法名称 → 算法配置 → type → SPI → 具体算法实现

  这就是 5.x 很重要的架构变化。

不同版本对 Precise 和 Range的影响

  以前:

1
2
3
4
5
Standard Strategy

├── PreciseShardingAlgorithm

└── RangeShardingAlgorithm

  现在层级抽离为传递参数,不同算法分别处理这两类参数:

1
2
3
4
5
6
7
8
9
10
11
12
Standard Strategy


ShardingAlgorithm

├── 精确分片值
│ ↓
│ PreciseShardingValue

└── 范围分片值

RangeShardingValue

  也就是说:Precise / Range 不再简单理解成“两种独立的算法类型接口”存在多种实现,而更多体现为 Standard Algorithm 接收到的两种不同分片值。

  ShardingSphere 5.x 将“分片策略”和“分片算法”进一步解耦:Strategy 负责组织分片键和路由流程,ShardingAlgorithm 负责具体分片计算,算法通过 type + props 配置并由 SPI 机制加载;Standard 根据 SQL 条件形成 Precise/Range 分片值,再交给所配置的 Algorithm 处理。

策略、匹配条件、算法三个维度差异

  当分片策略采用 Standard 策略时,Standard 用来确定单键策略,之后 Standard 根据 SQL 匹配条件构造为 Precise/Range,最后交给所配置的 Algorithm 处理路由。

  所以说 Standard 策略、Precise/Range 分片值、Algorithm,她们是三个维度的东西,Strategy 管“规则怎么组织”,Precise/Range 管“这次 SQL 给了什么类型的条件”,Algorithm 管“拿这个条件怎么算目标分片”。

  下面,我们用一个最简单的例子加深理解。

案例

  假设你的 GPS 表gps_track,数据库按照worker_id分片,配置:

1
2
3
standard:
sharding-column: worker_id
sharding-algorithm-name: worker-db-inline

  算法:

1
2
3
4
worker-db-inline:
type: INLINE
props:
algorithm-expression: ds_${worker_id % 4}

  然后执行 SQL SELECT * FROM gps_track WHERE worker_id = 10001;

  整个过程逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
① Strategy
Standard

“我的分片键是 worker_id”

② SQL条件
worker_id = 10001

“这是一个精确值”

PreciseShardingValue

③ Algorithm(接收 PreciseShardingValue 参数)
INLINE

10001 % 4

ds_1

  所以:

  • Standard → 决定“怎么组织分片”
  • Precise → 描述“这次条件是什么样”
  • INLINE → 决定“具体怎么算”

第一维:Standard Strategy —— “怎么组织分片”

  首先看Standard Strategy,它属于分片策略 Strategy,它主要解决的问题是:“我要根据哪个分片键进行标准分片,以及如何把 SQL 中的分片条件交给算法。”

  例如:

1
2
3
standard:
sharding-column: worker_id
sharding-algorithm-name: worker-db-inline

  这里 Standard 告诉 ShardingSphere:我的分片键为worker_id、我的算法为worker-db-inline,所以 Standard 更像一个路由组织者 / 调度者。它自己不是10001 % 4,也不是10000 ~ 20000,它只是负责把这些信息组织起来。

第二维:Precise / Range —— “这次 SQL 给了什么条件”

  这是另外一个层次。

  例如WHERE worker_id = 10001的 SQL 给出了一个明确的值,所以形成PreciseShardingValue,可以理解成:

1
2
3
4
5
6
7
worker_id = 10001



PreciseShardingValue {
worker_id = 10001
}

  而WHERE worker_id BETWEEN 10000 AND 20000SQL 给出的不是一个具体值,而是一个范围,所以形成RangeShardingValue,可以理解成:

1
worker_id ∈ [10000, 20000]

  所以Precise / Range回答的是:“这一次 SQL 给我的分片条件是什么类型?”而不是“应该使用什么算法?”

第三维:Algorithm —— “具体怎么算”

  现在已经知道worker_id = 10001是一个 Precise 条件,接下来还需要回答:10001 到底应该去哪一个数据库?这就是 Algorithm 的工作。

  例如:

1
2
type: INLINE
algorithm-expression: ds_${worker_id % 4}

  于是:

1
2
3
4
5
6
7
10001

10001 % 4

1

ds_1

  所以 Algorithm 回答的是“给我分片条件,我如何计算目标数据库/表?”

算法中的代码处理

  以取模分片算法ModShardingAlgorithm为例子,其存在两个方法分别处理精确值和范围区间:

1
2
3
4
5
6
7
8
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Comparable<?>> shardingValue) {
String shardingResultSuffix = this.getShardingResultSuffix(this.cutShardingValue(shardingValue.getValue()).mod(new BigInteger(String.valueOf(this.shardingCount))).toString());
return (String)ShardingAutoTableAlgorithmUtil.findMatchedTargetName(availableTargetNames, shardingResultSuffix, shardingValue.getDataNodeInfo()).orElse((Object)null);
}

public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Comparable<?>> shardingValue) {
return this.containsAllTargets(shardingValue) ? availableTargetNames : this.getAvailableTargetNames(availableTargetNames, shardingValue);
}

小节

概念 所属 回答的问题
Standard Strategy 怎么组织分片和路由
Precise / Range 分片条件类型 这次 SQL 给的是单值还是范围
Algorithm ShardingAlgorithm 根据分片条件怎么算目标分片

  可以把它们看成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
          SQL


┌──────────────────┐
│ Standard Strategy│
│ “怎么组织?” │
└────────┬─────────┘

SQL中的分片条件

┌──────┴──────┐
↓ ↓
Precise Range
“单值” “范围”
│ │
└──────┬──────┘

Algorithm
“怎么算?”


目标分片

  某种程度上,它们并不是完全“独立”平行的“三个维度”,而是一个调用链上的三个层次:选择 Strategy → 匹配分片条件 → 传递给 Algorithm 计算确定路由

1
2
3
4
5
第一步:Strategy

第二步:确定/组织分片条件

第三步:Algorithm计算

  所以“不同维度”更准确地说是:它们解决不同的问题,但会在同一条路由链路中协作。

通俗化理解“策略 + 算法”

  可以把它类比成物流系统

  分片策略 = 物流规则,告诉系统:“我应该根据什么信息来分配?”

  例如:

  • Standard:根据一个字段
  • Complex:根据多个字段
  • Hint:根据业务外部提供的信息

  分片算法 = 具体计算方法,告诉系统:“拿到这个信息之后,到底怎么算?”

  例如:

  • 普通的单键取模:user_id % 4
  • 常见的日期范围:create_time → 按月份
  • 复杂的多键自定义:employee_id + create_time → 自定义复杂计算

  所以完整关系就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
        分片策略

“分片条件怎么获取/组织?”


分片键/值


分片算法

“具体怎么算?”


实际数据节点

集成说明

添加依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.0</version>
</dependency>

修改配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# ---------------- 1. 数据源 ----------------
spring.shardingsphere.datasource.names=ds0,ds1
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/gps_db_0?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://127.0.0.1:3306/gps_db_1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456

# ---------------- 2. 逻辑表(员工表 + 案例① = 单节点;案例②~⑥分片) ----------------
# 员工表:非分片(单节点 ds0),业务主键 INPUT
spring.shardingsphere.rules.sharding.tables.employee.actual-data-nodes=ds0.employee

# ==================== 案例1:单表纪元(单节点表,主键 MP 雪花) ====================
spring.shardingsphere.rules.sharding.tables.track_one_ds_one_tb_raw.actual-data-nodes=ds0.track_one_ds_one_tb_raw

# ==================== 案例2:分库分表(员工) ====================
# 库=员工%2;表=(员工/2)%4(解耦公式避免分片空洞)
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.actual-data-nodes=ds$->{0..1}.track_mul_ds_eid_mul_tb_eid_$->{0..3}
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.database-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.database-strategy.standard.sharding-algorithm-name=algo-db-eid
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.table-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.table-strategy.standard.sharding-algorithm-name=algo-case2-tbl
# (已由 default-key-generate-strategy 全局统一配置)
# spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.key-generate-strategy.column=id
# spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.key-generate-strategy.key-generator-name=snowflake

# ==================== 案例3:分库(员工) + 按日分表 ====================
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.actual-data-nodes=ds$->{0..1}.track_mul_ds_eid_mul_tb_day_$->{20260726..20260806}
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.database-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.database-strategy.standard.sharding-algorithm-name=algo-db-eid
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.table-strategy.standard.sharding-column=gps_time
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.table-strategy.standard.sharding-algorithm-name=algo-day-interval

# ==================== 案例4:单库分表(员工) ====================
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid.actual-data-nodes=ds0.track_one_ds_mul_tb_eid_$->{0..3}
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid.table-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid.table-strategy.standard.sharding-algorithm-name=algo-case4-tbl

# ==================== 案例5:单库按日分表 ====================
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_day.actual-data-nodes=ds0.track_one_ds_mul_tb_day_$->{20260726..20260806}
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_day.table-strategy.standard.sharding-column=gps_time
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_day.table-strategy.standard.sharding-algorithm-name=algo-day-interval

# ==================== 案例6:单库混合(员工桶 × 日期, 复合分片键) ====================
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid_day.actual-data-nodes=ds0.track_one_ds_mul_tb_eid_day_$->{0..1}_$->{20260726..20260806}
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid_day.table-strategy.complex.sharding-columns=employee_id,gps_time
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid_day.table-strategy.complex.sharding-algorithm-name=algo-case6-complex
# (已由 default-key-generate-strategy 全局统一配置)
# spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid_day.key-generate-strategy.column=id
# spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid_day.key-generate-strategy.key-generator-name=snowflake

# ---------------- 3. 分片算法 ----------------
# 员工取模分库(内置 INLINE)
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-db-eid.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-db-eid.props.algorithm-expression=ds$->{employee_id % 2}
# 案例2 分表(自定义类:库表解耦公式 (employee_id/2)%4)
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case2-tbl.type=CLASS_BASED
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case2-tbl.props.strategy=STANDARD
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case2-tbl.props.algorithmClassName=com.example.gps.sharding.MulDsEidMulTbEidShardingAlgorithm
# 案例4 分表(自定义类:employee_id%4)
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case4-tbl.type=CLASS_BASED
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case4-tbl.props.strategy=STANDARD
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case4-tbl.props.algorithmClassName=com.example.gps.sharding.OneDsMulTbEidShardingAlgorithm
# 按日分表:内置 INTERVAL(ShardingSphere 5.2.1 实测可用),datetime-lower 与数据窗口对齐。
# (早期在 5.4.1 曾遇 datetime-lower 解析 bug,故曾回退自定义 DayShardingAlgorithm——该类仍保留作为教学参考)
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.type=INTERVAL
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-pattern=yyyy-MM-dd HH:mm:ss
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-lower=2026-07-26 00:00:00
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-upper=2026-08-07 00:00:00
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.sharding-suffix-pattern=yyyyMMdd
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-interval-unit=DAYS
# 案例6 复合分片(自定义类:员工桶 × 日期)
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case6-complex.type=CLASS_BASED
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case6-complex.props.strategy=COMPLEX
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case6-complex.props.algorithmClassName=com.example.gps.sharding.OneDsMulTbEidDayShardingAlgorithm

# ---------------- 4. 主键生成器(ShardingSphere SNOWFLAKE) ----------------
# 全局默认主键策略:分片规则内所有表统一用 snowflake 生成 id,
# 无需为每个案例单独配置(employee / track_one_ds_one_tb_raw 显式传 id,不受影响)。
spring.shardingsphere.rules.sharding.default-key-generate-strategy.column=id
spring.shardingsphere.rules.sharding.default-key-generate-strategy.key-generator-name=snowflake
spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=1

# ---------------- 5. 其它 ----------------
spring.shardingsphere.props.sql-show=true

踩坑记录

  • Guava 冲突
  • 配置文件各数据源必须指定 type
  • Druid starter 冲突,需去除
  • 需要额外引入以下依赖
1
2
3
4
5
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>10.0.16</version>
</dependency>

实战案例

  下面通过实战案例演示下,同一个业务(环卫工人 GPS 轨迹,按”某员工某天”查询),用 多种不同的表切分方案进行分库分表。

  具体演示见sharding-track项目。

「员工 id 分库分表」

  此案例下,既用员工 id 分库,又用员工 id 分表,两层都用员工 ID 的哈希取模:

  • 库 = HASH_MOD(employee_id, 2);表 = HASH_MOD(employee_id, 5)(每库 5 张,共 10 张)。
  • 注意:两层取模必须互质,这样 (库,表) 的 10 种组合都能出现,10 张表全部生效;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ==================== 案例2:分库分表(员工) ====================
# 库=HASH_MOD(employee_id,2);表=HASH_MOD(employee_id,5)
# 教学点:两层取模必须互质——gcd(2,5)=1,(库,表) 10 种组合都能出现,10 张表均匀;
# 若表层用 4 则 hash%2 与 hash%4 相关(hash%2 ≡ (hash%4)%2),8 张表只会用到 4 张(分片空洞)。
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.actual-data-nodes=ds$->{0..1}.track_mul_ds_eid_mul_tb_eid_$->{0..4}
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.database-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.database-strategy.standard.sharding-algorithm-name=algo-hash-eid-2
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.table-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_eid.table-strategy.standard.sharding-algorithm-name=algo-hash-eid-5

# 员工哈希取模分库/分表(内置 HASH_MOD;本版本对整数键落位等价于 值 % count,已实测验证)
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-hash-eid-2.type=HASH_MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-hash-eid-2.props.sharding-count=2
# 案例② 表层:每库 5 张表(与库层 2 互质,消除分片空洞)
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-hash-eid-5.type=HASH_MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-hash-eid-5.props.sharding-count=5

「员工分库 + 按日分表」

  此案例下,员工 id 分库,轨迹创建时间按日分表:

  • employee_id 定库(员工维度),gps_time 定日表(时间维度),两层各管一件事。
  • “某员工某天” = 1 库 1 张日表;”某员工近 N 天” = N 张日表
  • 生产价值:历史按天 DROP/归档,过期数据零成本清理;单张日表体量恒定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.actual-data-nodes=ds$->{0..1}.track_mul_ds_eid_mul_tb_day_$->{20260726..20260806}
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.database-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.database-strategy.standard.sharding-algorithm-name=algo-hash-eid-2
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.table-strategy.standard.sharding-column=gps_time
spring.shardingsphere.rules.sharding.tables.track_mul_ds_eid_mul_tb_day.table-strategy.standard.sharding-algorithm-name=algo-day-interval

spring.shardingsphere.rules.sharding.sharding-algorithms.algo-hash-eid-2.type=HASH_MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-hash-eid-2.props.sharding-count=2

# 按日分表:内置 INTERVAL(ShardingSphere 5.2.1 实测可用),datetime-lower 与数据窗口对齐。
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.type=INTERVAL
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-pattern=yyyy-MM-dd HH:mm:ss
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-lower=2026-07-26 00:00:00
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-upper=2026-08-07 00:00:00
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.sharding-suffix-pattern=yyyyMMdd
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-interval-unit=DAYS

「单库员工 id 分表」

  此案例下,只有一个数据库,之后用员工 id 分多张表

1
2
3
4
5
6
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid.actual-data-nodes=ds0.track_one_ds_mul_tb_eid_$->{0..3}
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid.table-strategy.standard.sharding-column=employee_id
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid.table-strategy.standard.sharding-algorithm-name=algo-hash-eid-4

spring.shardingsphere.rules.sharding.sharding-algorithms.algo-hash-eid-4.type=HASH_MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-hash-eid-4.props.sharding-count=4

「单库按时间一日一表」

  此案例下,只有一个数据库,之后用轨迹创建时间分多张表

  • 表 = gps_time 所在日,单库一日一表,表需要提前创建
  • 按天归档时,若”某员工某天”没有员工等值可路由,先按日期命中日表、再在表内过滤员工
1
2
3
4
5
6
7
8
9
10
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_day.actual-data-nodes=ds0.track_one_ds_mul_tb_day_$->{20260726..20260806}
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_day.table-strategy.standard.sharding-column=gps_time
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_day.table-strategy.standard.sharding-algorithm-name=algo-day-interval

spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.type=INTERVAL
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-pattern=yyyy-MM-dd HH:mm:ss
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-lower=2026-07-26 00:00:00
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-upper=2026-08-07 00:00:00
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.sharding-suffix-pattern=yyyyMMdd
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-day-interval.props.datetime-interval-unit=DAYS

「单库员工×日复合分片键分表」

  此案例下,只有一个数据库,但是分表时,复合处理:

  • 先按员工ID哈希取模分桶(5 个桶 → 每天 5 张表),再按日表定位,两个维度同时决定表。
  • 需要自定义复合分片算法OneDsMulTbEidDayShardingAlgorithm,COMPLEX 策略)——
  • 单员工单日精确命中 1 张最小切片表:既有员工点查优势,又支持按天归档
1
2
3
4
5
6
7
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid_day.actual-data-nodes=ds0.track_one_ds_mul_tb_eid_day_$->{0..4}_$->{20260726..20260806}
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid_day.table-strategy.complex.sharding-columns=employee_id,gps_time
spring.shardingsphere.rules.sharding.tables.track_one_ds_mul_tb_eid_day.table-strategy.complex.sharding-algorithm-name=algo-case6-complex

spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case6-complex.type=CLASS_BASED
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case6-complex.props.strategy=COMPLEX
spring.shardingsphere.rules.sharding.sharding-algorithms.algo-case6-complex.props.algorithmClassName=com.example.gps.sharding.OneDsMulTbEidDayShardingAlgorithm

思考

集成后是否影响原有逻辑?

  项目目集成 Shardingsphere-jdbc 后,对于原先的单表有什么影响,比如查询这块,还是走的代表查询吗?

  集成 ShardingSphere-JDBC 后,原来的单表不会自动变成“分片表”。只要没有给这张表配置分片规则,它仍然按照单表方式访问。

  可以简单理解为:ShardingSphere-JDBC 是在原 JDBC 上增加了一层 SQL 解析 + 路由。没有分片规则的表,就直接路由到对应数据源执行。

原来的普通单表

  例如原来有 SQL 查询SELECT * FROM sys_user WHERE id = 1;,会通过数据库直接到sys_user

  现在,接入 ShardingSphere 后,逻辑变为这样:

1
2
3
4
5
6
7
8
9
10
11
业务代码

ShardingSphere-JDBC

判断 sys_user 是否配置了分片规则

没有

直接路由到对应数据源

mysql.sys_user

  所以 SQL 本身基本不用修改

如果是分片表

例如执行SELECT * FROM t_order WHERE order_id = 100;,如果有分了多张表:

1
2
3
4
5
t_order
├── t_order_0
├── t_order_1
├── t_order_2
└── t_order_3

  ShardingSphere 会根据分片规则计算:

1
2
3
4
5
order_id = 100

分片算法

t_order_0

  然后实际查询:

1
SELECT * FROM t_order_0 WHERE order_id = 100;

  这才涉及真正的分片路由。官方配置中也是通过 actualDataNodesdatabaseStrategytableStrategy 等配置描述逻辑表和实际节点。

项目中通常会同时存在两类表

类型 示例 查询方式
单表 sys_usersys_dict 直接路由到对应数据源
分片表 t_order_0 ~ t_order_15 根据分片键计算具体库表
广播表 sys_region 根据广播规则处理多个数据源

  ShardingSphere 官方也把没有进行分片的表称为 Single Table(单表)

对原有业务最大的影响

  实际上主要是这几个方面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
原来的:

MyBatis

DataSource

MySQL


接入 ShardingSphere:

MyBatis

ShardingSphereDataSource

SQL解析

┌──────────────┬──────────────┐
│ 单表 │ 分片表 │
│ 直接路由 │ 分片计算 │
└──────────────┴──────────────┘

MySQL

  因此,如果项目只是新增 ShardingSphere-JDBC,同时原有大量普通表不参与分片,这些表的 SQL 一般不需要改,仍然是正常的单表查询。

  不过有一个需要注意的版本问题:ShardingSphere 5.4.0 起,Single Table 的加载方式发生了调整,需要通过 YAML 的 !SINGLE 或 DistSQL 显式加载单表,不能简单理解成所有数据库中的普通表都会被自动识别。

文章信息

时间 说明
2026-04-22 初稿
0%